package com.sh.cloudrender.domian.service.kube;

import com.sh.cloudrender.domian.entity.TraefikMiddleware;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class TraefikService {

    private static final String ReplaceHostMiddleware = "engine-replace-host";
    private static final String ReplacePathMiddleware = "engine-replace-path";
    private static final String TRAEFIK_GROUP = "traefik.containo.us";
    private static final String TRAEFIK_VERSION = "v1alpha1";

    @Autowired
    private CustomObjectsApi customObjectsApi;

    public boolean createIngressRoute(String ingressRouteName, String namespace, String pathPrefix, String backendSvc, Integer backendPort) {
        log.info("create ingress route param, ingressRouteName :{}, namespace : {} , pathPrefix : {} , backendSvc : {} , backendPort : {}  ", ingressRouteName, namespace, pathPrefix, backendSvc, backendPort);
        // 构建 IngressRoute 对象
        Map<String, Object> ingressRoute = new HashMap<>();
        ingressRoute.put("apiVersion", "traefik.containo.us/v1alpha1");
        ingressRoute.put("kind", "IngressRoute");

        Map<String, Object> metadata = new HashMap<>();
        metadata.put("name", ingressRouteName);
        metadata.put("namespace", namespace);
        ingressRoute.put("metadata", metadata);

        Map<String, Object> service = new HashMap<>();
        service.put("name", backendSvc);
        service.put("port", backendPort);

        Map<String, Object> route = new HashMap<>();
        route.put("kind", "Rule");
        route.put("match", "PathPrefix(`" + pathPrefix + "`)");
        route.put("services", new Object[]{service});
        Map<String, Object> middleware = new HashMap<>();
        middleware.put("name", ReplaceHostMiddleware);
        middleware.put("name", ReplacePathMiddleware);
        route.put("middlewares", new Object[]{middleware});

        Map<String, Object> spec = new HashMap<>();
        spec.put("entryPoints", new String[]{"web", "websecure"});
        spec.put("routes", new Object[]{route});
        ingressRoute.put("spec", spec);
        Object result = null;
        try {
            result = customObjectsApi.createNamespacedCustomObject(
                    TRAEFIK_GROUP,
                    TRAEFIK_VERSION,
                    namespace,
                    "ingressroutes",
                    ingressRoute,
                    null,
                    null,
                    null);

        } catch (ApiException e) {
            e.printStackTrace();
            log.error("create ingress route error,  ingressRoute : {} , msg:{}", ingressRoute, e.getResponseBody());
            return false;
        }

        if (result != null) {
            return true;
        }

        return false;
    }

    public boolean deleteIngressRoute(String ingressRouteName, String namespace) {
        try {
            customObjectsApi.deleteNamespacedCustomObject(
                    TRAEFIK_GROUP,
                    TRAEFIK_VERSION,
                    namespace,
                    "ingressroutes",
                    ingressRouteName,
                    null,
                    null,
                    null,
                    null,
                    null
            );
        } catch (ApiException e) {
            log.error("delete ingress route error, msg:{}", e.getResponseBody());
            return false;
        }

        return true;
    }

    public boolean deleteMiddleware(String middlewareName, String namespace) {
        try {
            customObjectsApi.deleteNamespacedCustomObject(
                    TRAEFIK_GROUP,
                    TRAEFIK_VERSION,
                    namespace,
                    "middlewares",
                    middlewareName,
                    null,
                    null,
                    null,
                    null,
                    null
            );
        } catch (ApiException e) {
            log.error("delete middleware error, msg:{}", e.getResponseBody());
            return false;
        }

        return true;
    }

    public void createMiddlewareFromYamlResource(String namespace, String yamlResourcePath) {
        List<TraefikMiddleware> middlewares = parseYamlToMiddleware(yamlResourcePath);
        if (CollectionUtils.isEmpty(middlewares)) {
            return;
        }

        try {
            for (TraefikMiddleware traefikMiddleware : middlewares) {
                customObjectsApi.createNamespacedCustomObject(
                        TRAEFIK_GROUP,
                        TRAEFIK_VERSION,
                        namespace,
                        "middlewares",
                        traefikMiddleware,
                        null,
                        null,
                        null
                );
            }

        } catch (ApiException e) {
            log.warn("creating middleware from YAML resource failed may existed, msg:{}", e.getResponseBody());
        }
    }

    public static List<TraefikMiddleware> parseYamlToMiddleware(String yamlFile) {
        List<TraefikMiddleware> middlewares = new ArrayList<>();
        Yaml yaml = new Yaml();

        try (InputStream inputStream = TraefikService.class.getResourceAsStream(yamlFile)) {
            Iterable<Object> yamlObjects = yaml.loadAll(inputStream);
            for (Object yamlObject : yamlObjects) {
                Map<String, Object> yamlMap = (Map<String, Object>) yamlObject;
                TraefikMiddleware middleware = createTraefikMiddlewareFromYamlMap(yamlMap);
                middlewares.add(middleware);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return middlewares;
    }

    private static TraefikMiddleware createTraefikMiddlewareFromYamlMap(Map<String, Object> yamlMap) {
        TraefikMiddleware middleware = new TraefikMiddleware();

        // Set API version and kind
        middleware.setApiVersion((String) yamlMap.get("apiVersion"));
        middleware.setKind((String) yamlMap.get("kind"));

        // Set metadata
        V1ObjectMeta metadata = new V1ObjectMeta();
        Map<String, String> metadataMap = (Map<String, String>) yamlMap.get("metadata");
        metadata.setName(metadataMap.get("name"));
        metadata.setNamespace(metadataMap.get("namespace"));
        middleware.setMetadata(metadata);

        // Set spec
        TraefikMiddleware.Spec spec = new TraefikMiddleware.Spec();

        // Check and set replacePathRegex or headers based on kind
        if ("Middleware".equals(middleware.getKind())) {
            Map<String, Object> specMap = (Map<String, Object>) yamlMap.get("spec");

            if (specMap.containsKey("replacePathRegex")) {
                Map<String, String> replacePathRegexMap = (Map<String, String>) specMap.get("replacePathRegex");
                TraefikMiddleware.ReplacePathRegex replacePathRegex = new TraefikMiddleware.ReplacePathRegex();
                replacePathRegex.setRegex(replacePathRegexMap.get("regex"));
                replacePathRegex.setReplacement(replacePathRegexMap.get("replacement"));
                spec.setReplacePathRegex(replacePathRegex);
            }

            if (specMap.containsKey("headers")) {
                Map<String, Map<String, String>> headersMap = (Map<String, Map<String, String>>) specMap.get("headers");
                TraefikMiddleware.Headers headers = new TraefikMiddleware.Headers();
                TraefikMiddleware.CustomRequestHeaders customRequestHeaders = new TraefikMiddleware.CustomRequestHeaders();
                customRequestHeaders.setHost(headersMap.get("customRequestHeaders").get("Host"));
                headers.setCustomRequestHeaders(customRequestHeaders);
                spec.setHeaders(headers);
            }
        }

        middleware.setSpec(spec);

        return middleware;
    }
}
